/* main.c */

#include <avr/io.h>
#include "eeprom.h"
#include "usart.h"
#include "delay.h"

#define DS18B20_PORT PORTB
#define DS18B20_DDR  DDRB
#define DS18B20_PIN  PINB
#define DS18B20_DQ   PB2

#define DS18B20_INPUT_MODE()  (DS18B20_DDR &=~ (1 << DS18B20_DQ))
#define DS18B20_OUTPUT_MODE() (DS18B20_DDR |= (1 << DS18B20_DQ))
#define DS18B20_LOW()         (DS18B20_PORT &=~ (1 << DS18B20_DQ))
#define DS18B20_HIGH()        (DS18B20_PORT |= (1 << DS18B20_DQ))

#define DS18B20_CMD_SKIP_ROM 0xcc
#define DS18B20_CMD_CONVERT_TEMP 0x44
#define DS18B20_CMD_READ_SCRATCHPAD 0xbe

void onewire_write_bit(uint8_t bit)
{
   DS18B20_LOW();
   DS18B20_OUTPUT_MODE();
   delay_us(1);

   if (bit) {
      DS18B20_INPUT_MODE();
   }

   delay_us(60);
   DS18B20_INPUT_MODE();
}

void onewire_write(uint8_t c)
{
   uint8_t i = 8;
   
   while (i--) {
      onewire_write_bit(c & 1);
      c >>= 1;
   }
}

uint8_t onewire_read_bit()
{
   uint8_t bit=0; 
   
   //Pull line low for 1uS 
   DS18B20_LOW(); 
   DS18B20_OUTPUT_MODE(); 
   delay_us(1);
   
   //Release line and wait for 14uS 
   DS18B20_INPUT_MODE(); 
   delay_us(14);
   
   //Read line value 
   if (DS18B20_PIN & (1 << DS18B20_DQ)) {
      bit=1;
   }
   
   //Wait for 45uS to end and return read value 
   delay_us(45);
   
   return bit;
}

uint8_t onewire_read()
{
   uint8_t i = 8, n = 0;

   while (i--) {
      n >>= 1;
      n |= (onewire_read_bit() << 7);
   }

   return n;
}

uint8_t onewire_reset()
{
   uint8_t i;
   
   //DS18B20_INPUT_MODE();
   //delay_ms(5);
   //if ((DS18B20_PIN & (1 << DS18B20_DQ)) == 0) {
   //   return 1;
   //}
   
   DS18B20_LOW();
   DS18B20_OUTPUT_MODE();
   delay_us(240);
   delay_us(240);
   
   DS18B20_INPUT_MODE();
   delay_us(60);
   
   i = (DS18B20_PIN & (1 << DS18B20_DQ));
   delay_us(210);
   delay_us(210);
   
   return i;
   
#if 0
   // Pull pin high

   cbi(DDRB, PB2);

   for (uint8_t n = 0; n < 255; n++) {
      delay_us(2);
      if (PINB & 0b00000100) {
         break;
      }
      if (n == 254) {
         usart_tx_string("Nope");
         return 0;
      }
   }

   // Pull pin low for 500 micro seconds

   sbi(PORTB, PB2);
   sbi(DDRB, PB2);
   
   delay_us(250);
   delay_us(250);
   
   // Check the status of the input pin, it should be high
   
   uint8_t result = 0;
   
   sbi(DDRB, PB2);
   delay_us(65);
   result = PORTB & 0b00000100;
   delay_us(255);
   delay_us(235);

   return result;
#endif
}

uint8_t ds18b20_find(/* uint8_t* port, uint8_t* ddr, uint8_t pin,*/ uint8_t* id)
{
   uint8_t result = onewire_reset();
   if (result == 0) {
      onewire_write(0x33);
      for (uint8_t n = 0; n < 8; n++) {
         id[n] = onewire_read();
      }
   }
   return result;
}

uint8_t ds18b20_query(/* uint8_t* port, uint8_t* ddr, uint8_t pin,*/ uint8_t* temp)
{
   uint8_t result = onewire_reset();
   if (result == 0)
   {
      onewire_write(DS18B20_CMD_SKIP_ROM);
      onewire_write(DS18B20_CMD_CONVERT_TEMP);
      
      while (!onewire_read_bit()) {
         // Wait
      }

      result = onewire_reset();
      if (result == 0)
      {
         onewire_write(DS18B20_CMD_SKIP_ROM);
         onewire_write(DS18B20_CMD_READ_SCRATCHPAD);
         temp[0] = onewire_read();
         temp[1] = onewire_read();
      }
   }
   
   return result;
}

void main(void)
{
   usart_init(16000000 / 16 / 9600 - 1);

#if 0
   usart_tx_string("=============\r\n");

   //for (unsigned char n = 0; n < 255; n++) {
   //   usart_tx_byte(n);
   //   usart_tx_string("\r\n");
   //}

   for (char n = 127; n >= -127; n--) {
      usart_tx_sbyte(n);
      usart_tx_string("\r\n");
   }

   usart_tx_string("=============\r\n");

   while (1) {};
#endif

   usart_tx(22);
   delay_ms(10);

   int8_t last = 0;
   int8_t low = 127;
   int8_t high = -127;
   
   while (1)
   {
      unsigned char temp[2];
      if (ds18b20_query(/*&PORTB, &DDRB, PB2, */ temp) == 0)
      {
         int8_t t = ((temp[0] >> 4) | (temp[1] << 4)) | (temp[1] & 0b10000000);
         
         if (t != last)
         {
            if (t > high) {
               high = t;
            }
            
            if (t < low) {
               low = t;
            }
            
            usart_tx(12);
            delay_ms(10);
            usart_tx_string("Temperature ");
            usart_tx_sbyte(t);
            usart_tx('\r');
            usart_tx_string("Low ");
            usart_tx_sbyte(low);
            usart_tx_string(" High ");
            usart_tx_sbyte(high);

            last = t;
         }
      } else {
         usart_tx_string("No sensor found");
      }

      delay(5);
   }
}
